import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import GUI from 'lil-gui'
type AnimationCallbackType = ((t: number) => void)| null

const initThree = (dom: HTMLElement) => {
  // 初始化相机
  const camera = new THREE.PerspectiveCamera(70, dom.offsetWidth / dom.offsetHeight, 0.01, 1000)
  // 初始化场景
  const scene = new THREE.Scene()
  // 初始化渲染器
  const renderer = new THREE.WebGLRenderer({ antialias: true })
  // 设置画布大小，跟传入的容器大小一致
  renderer.setSize(dom.offsetWidth, dom.offsetHeight)
  // 设置动画
  renderer.setAnimationLoop(animation)
  // 将画布加入到容器中
  dom.appendChild(renderer.domElement)

  const orbitControls = new OrbitControls(camera, renderer.domElement)
  console.log(orbitControls)

  // 动画回调
  let animationCallback: AnimationCallbackType
  function update (callback: (t: number) => void) {
    animationCallback = callback
  }
  // 动画函数，当我们传入动画回调update的回调函数时，能自执行动画
  function animation (time: number) {
    animationCallback && animationCallback(time)
    renderer.render(scene, camera)
  }

  // 创建GUI调试工具
  const gui = new GUI({
    container: dom
  })

  window.addEventListener('resize', windowResize, false)

  function windowResize () {
    camera.aspect = dom.offsetWidth / dom.offsetHeight
    camera.updateProjectionMatrix()
    renderer.setSize(dom.offsetWidth, dom.offsetHeight)
  }
  return {
    camera,
    scene,
    renderer,
    dom,
    gui,
    update,
    orbitControls
  }
}

interface AppType {
  camera: THREE.PerspectiveCamera
  scene: THREE.Scene
  renderer: THREE.WebGLRenderer
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  update: any
  dom: HTMLElement,
  gui: GUI,
  orbitControls: OrbitControls
}

// 重置相机
function resetCamera (app: AppType) {
  const { camera, orbitControls } = app
  camera.position.set(0, 0, 10)
  camera.lookAt(0, 0, 0)
  orbitControls.target.set(0, 0, 0)
}

// 重置场景
function resetScene (app: AppType) {
  const { scene } = app
  const objs = [...scene.children]
  objs.forEach(obj => {
    scene.remove(obj)
  })
  scene.background = null
}

// 清空gui
function clearGUI (gui: GUI) {
  const folders = [...gui.folders]
  const controllers = [...gui.controllers]
  folders.forEach(folder => {
    folder.destroy()
  })
  controllers.forEach(controller => {
    controller.destroy()
  })
}

function clearAnimation (app: AppType) {
  const { update } = app
  update()
}

export default (function init () {
  let app: AppType
  return function (dom: HTMLElement) {
    if (app) {
      resetScene(app)
      resetCamera(app)
      clearAnimation(app)
      clearGUI(app.gui)
      dom.appendChild(app.renderer.domElement)
      return app
    }
    app = initThree(dom)
    return app
  }
})()
